---
title: C语言基础
---
## 一段简单的代码分析
```c title='输出单词 Hello World'
# include <stdio.h>
int main() { // 程序里面有且仅有一个主函数
    printf("Hello,World!\n");
    return 0;
}
```

程序的第一行 `#include <stdio.h>` 是预处理器指令，告诉 C 编译器在实际编译之前要包含 `stdio.h` 文件。

第二行 `int main()` 是主函数，程序在这里执行，**C语言程序都是从 main 函数开始执行**。

第三行 `printf(...)` 是 C 中另一个可用的函数，会在屏幕上显示消息 “Hello World”。

最后一行 `return 0;` 终止 `main()` 函数，并返回值 0 。

## 基础语法
* 英文分号 `;` ,在 C 程序中，<RedSpan>分号是语句结束符</RedSpan>。也就是说，<RedSpan>每个语句必须以分号结束</RedSpan>。

* C 语言中的注释有两种： 以 `//` 开始的单行注释，这种注释可以单独占一行 ； `/* */` 这种个数的注释可以单行或者多行；不能在注释内嵌套注释，注释也不能出现在字符串或字符值中。

* <RedSpan>C 标识符是用来标识变量、函数，或任何其它用户自定义项目的名称。</RedSpan>
    * 一个标识符以字母 A-Z 或 a-z 或 下划线_ 开始，不能以数字开始，后面跟零个或多个字母、下划线和数字（0-9）。
    * C 标识符内不允许出现标点符号，比如 @、$ 和 %。
    * C 是<RedSpan>区分大小写</RedSpan>的编程语言。

* <RedSpan>C 语言自己保留的关键字</RedSpan>，编写程序时不能与之重复，如变量 定义 `int/char/double` 等保留字。

* C 中的空格
    * <RedSpan>只包含空格的行，被称为空白行</RedSpan>，可能带有注释，C 编译器会完全忽略它。
    * 在 C 中，空格用于描述空白符、制表符和注释。空格分割语句的各个部分，<RedSpan>让编译器能识别语句中的某个元素（比如 int），在哪里结束，下一个元素从哪里开始</RedSpan>。
    * `int age` ,int 和 age 之间必须至少有一个空格字符（通常是一个空白符），这样编译器才能够区分它们。
    * `fruit = apples + oranges; // 获取水果的总数` fruit 和 =，或者 = 和 apples 之间的空格字符不是必需的，但是为了增强可读性，可以根据需要适当增加一些空格。

* sizeof 获取存储字节： 为了得到某个类型或某个变量在特定平台的准确大小，可以使用 sizeof 运算符。表示式 <RedSpan>sizeof(type) 得到对象或类型的存储字节大小。</RedSpan>，比如： sizeof(int)、sizeof(12)

* void 类型用于 <RedSpan>指定没有可用的值</RedSpan>。它通常用于以下三种情况：




<Table
    style={{marginLeft: '30px'}}
    showHeader={false}
    columns={
        [
            {
                title: '',
                dataIndex: 'a1',
            },
            {
                title: '',
                dataIndex: 'a2',
                render: (value,record) => <div dangerouslySetInnerHTML = {{__html: value}} ></div>
            },
        ]
    }
    dataSource={[
        {
            a1: '1',
            a2: '函数返回值为空 <br/> C 中有各种函数都不返回值,不返回值的返回类型为空 <br/> 例如： void exit(int status);'
        },
        {
            a1: '2',
            a2: '函数参数为空 <br/> C 中有各种函数不接受任何参数。不带参数可以接受一个 void，可以直接不写。 <br/> 例如 int rand(void);'
        },
        {
            a1: '3',
            a2: '指针指向 void <br/> 类型为 void* 的指针代表对象的地址，而不是类型。 <br/>' +
                '例如，内存分配函数 void *  malloc(size_tsize)；返回指向 void 的指针，可以转换为任意类型'
        },
    ]}
    bordered
    pagination={false}
/>

### 变量
变量其实只不过是<RedSpan>程序可操作的存储区的名称</RedSpan>。C 中每个变量都有特定的类型，<RedSpan>类型决定了变量的大小和布局</RedSpan>，该范围内的值可以存储在内存中，运算符可应用于变量上。

变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头。大写字母和小写字母是不同的，因为 C 是大小写敏感的。

变量定义：指定一个数据类型，并包含该类型的一个或多个变量的列表，如下所示：
```c
type variable_list;
```
在这里 type 必须是一个有效的 c 数据类型，可以是 <RedSpan>char、w_char、int、float、double 或任何用户自定义的对象</RedSpan>, variable_list 可以由一个或多个标识符名称组成，多个标识符之间用逗号分割。下面列出几个有效的声明：
```c
int i,j,k;
```

变量可以在声明的时候被初始化（指定一个初始值）。初始化器由一个等号，后跟一个常量表达式组成，如下所示：

```c
type variable_name = value;
```
下面是几个实例：

```c
int d = 3, f =5; // 定义并初始化整型变量 d 和 f
char x = 'x'; //字符型变量 x 的值为 x'
```

### 定义一维数组
定义一维数组
```c
type arrayName[arraySize]
```
### 定义枚举类型
<RedSpan> 枚举第一个元素默认为 0， 可以赋其它值</RedSpan>。

枚举元素的值 <RedSpan>默认在前一个元素基础上加1</RedSpan>；

```c title='定义枚举变量'
enum DAY {
    MON = 1,TUE,WED,THU,FRI,SAT,SUN
};
```
### 表达式
C 中有两种类型的表达式：

左值（lvalue）： <RedSpan>指向内存位置</RedSpan>的表达式被称为左值（lvalue）表达式。左值表达式可以出现在<RedSpan>赋值号的左边或右边</RedSpan>。

右值（rvalue）：术语右值（rvalue） 指的是<RedSpan>存储在内存中某些地址的数值</RedSpan>。右值是<RedSpan>不能对其进行赋值的表达式</RedSpan>，也就是说，右值可以出现在赋值号的右边，但不能出现在赋值号的左边。

### 常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数：0x 或 0X 表示十六进制，0表示八进制，不带前缀则默认表示十进制。

浮点常量由 <RedSpan>整数部分、小数点、小数部分和指数部分组成。</RedSpan>可以使用小数形式或者指数形式来表示浮点常量，如：
```c
3.14159
314159E-5
```

字符常量是括在单引号中，可以是一个普通的字符（例如 'x'）、一个转义序列（例如 '\t'）。
在 C 中，有一些特定的字符，当它们前面有反斜杠时，它们就具有特殊的含义，被用来表示如换行符 （\n） 或制表符 （\t）

<RedSpan>字符串的字面值或常量是括在双引号 "" 中的</RedSpan>。一个字符串包含类似于字符常量的字符： 普通字符、转义序列和通用的字符。

### 宏定义
 `#define` <RedSpan>用来定义一个可以代替值的宏</RedSpan>，语法格式如下：
```c
# define 宏名称 值
```

例如
```c
# define LENGTH 10 // 后续使用可用 LENGTH 代替 10 这个常量。
```

## 关键字
### const 关键字
const 关键字定义一个 <RedSpan>只读的变量</RedSpan>，本质是修改了变量的存储方式为只读。
```c
const int LENGTH = 10;// 定义变量 LENGTH = 10 ,且只读，即无法修改 LENGTH 的值。
```
### static 关键字
<RedSpan>使用 static 修饰局部变量可以在函数调用之间保持局部变量的值</RedSpan>，这种方式不推荐。

static 修饰符 <RedSpan>也可以应用于全局变量或函数</RedSpan>。当 static 修饰全局变量或函数时，会使变量或函数的作用域限制在声明它的文件内。

避免被其它文件误用。
### extern 关键字
extern 关键字用于 <RedSpan>提供一个全局变量的引用</RedSpan>，全局变量对所有的程序文件都是可见的。

可以这么理解，extern 是 <RedSpan>用来在另一个文件中声明一个全局变量或函数。</RedSpan>extern 修饰符通常用于当有 <RedSpan>两个或多个文件共享相同的全局变量或函数的时候</RedSpan>

### typedef 关键字
typedef 关键字为 <RedSpan>类型取一个新的名字</RedSpan>。下面的实例为单字节数字定义了一个术语 `BYTE： typedef unsigned char BYTE`

在这个类型定义之后，标识符 BYTE 可以作为类型 unsigned char 的缩写，例如： BYTE b1,b2;

:::tip
#define 为值取一个名字，typedef 为类型取一个名字。
:::

## 运算符
### 算数运算符
下表显示了 C 语言支持的所有算数运算符。假设变量 A 的值为 10，变量 B 的值 为 20 ，则：


<Table
    columns={
        [
            {
                title: '运算符',
                dataIndex: 'a1',
            },
            {
                title: '描述',
                dataIndex: 'a2',
            },
            {
                title: '实例',
                dataIndex: 'a3',
            },
        ]
    }
    dataSource={[
        {
            a1: '+',
            a2: '把两个操作数相加',
            a3: 'A+B = 30 ',
        },
        {
            a1: '-',
            a2: '把两个操作数相减',
            a3: 'A-B = -10 ',
        },
        {
            a1: '*',
            a2: '把两个操作数相乘',
            a3: 'A*B = 200 ',
        },
        {
            a1: '÷',
            a2: '分子除以分母',
            a3: 'B / A = 2 ',
        },
        {
            a1: '%',
            a2: '取模运算符，整除后的余数',
            a3: 'B % A = 0 ',
        },
        {
            a1: '++',
            a2: '自增运算符，整数值增加 1',
            a3: 'A++ 将得到 11 ',
        },
        {
            a1: '--',
            a2: '自减运算符，整数值减少 1',
            a3: 'A-- 将得到 9',
        }
    ]}
    bordered
    pagination={false}
/>

### 关系运算符
下表展示了 C 语言支持的所有关系运算符。假设变量 A 的值为  10，变量 B 的值为 20 ，则：

<Table
    columns={
        [
            {
                title: '运算符',
                dataIndex: 'a1',
            },
            {
                title: '描述',
                dataIndex: 'a2',
            },
            {
                title: '实例',
                dataIndex: 'a3',
            },
        ]
    }
    dataSource={[
        {
            a1: '==',
            a2: '检查两个操作数的值是否相等，如果相等则条件为真',
            a3: '(A == B) 不为真',
        },
        {
            a1: '!=',
            a2: '检查两个操作数的值是否相等，如果不相等则条件为真',
            a3: '(A!=B)为真',
        },
        {
            a1: '>',
            a2: '检查左操作数的值是否大于右操作数，如果是则条件为真',
            a3: '(A>B 不为真)',
        },
        {
            a1: '<',
            a2: '检查左操作数的值是否小于右操作数的值，如果是则条件为真',
            a3: '(A<B) 为真 ',
        },
        {
            a1: '>=',
            a2: '检查左操作数的值是否大于等于右操作数的值，如果是则条件为真',
            a3: '(A >= B) 不为真',
        },
        {
            a1: '<=',
            a2: '检查左操作数的值是否小于等于右操作数的值，如果是则条件为真',
            a3: '(A <= B) 为真',
        },
    ]}
    bordered
    pagination={false}
/>


### 逻辑运算符

下表显示了  C 语言支持的所有逻辑运算符。假设变量 A 的值为 1，变量 B 的值为 0，则：

<Table
    columns={
        [
            {
                title: '运算符',
                dataIndex: 'a1',
            },
            {
                title: '描述',
                dataIndex: 'a2',
            },
            {
                title: '实例',
                dataIndex: 'a3',
            },
        ]
    }
    dataSource={[
        {
            a1: '&&',
            a2: '称为逻辑与运算符。如果两个操作数都为真，则为真',
            a3: '(A && B) 不为真',
        },
        {
            a1: '||',
            a2: '称为逻辑或运算符，如果两个操作符中有一个为真，则条件为真',
            a3: '(A || B)为真',
        },
        {
            a1: '!',
            a2: '称为逻辑非运算符，用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假',
            a3: '!(A && B)为真 ',
        }
    ]}
    bordered
    pagination={false}
/>


### 杂项运算符
下表列出了 C 语言支持的其它一些重要的运算符，包括 sizeof 和 ?:

<Table
    columns={
        [
            {
                title: '运算符',
                dataIndex: 'a1',
            },
            {
                title: '描述',
                dataIndex: 'a2',
            },
            {
                title: '实例',
                dataIndex: 'a3',
            },
        ]
    }
    dataSource={[
        {
            a1: 'sizeof()',
            a2: '返回变量的大小，字节大小',
            a3: 'sizeof(a) 将返回 4，其中 a 是整数。',
        },
        {
            a1: '&',
            a2: '返回变量的地址，取地址符号',
            a3: '&a; 将给出变量的实际地址',
        },
        {
            a1: '*',
            a2: '指向一个变量',
            a3: '*a; 将指向一个变量 ',
        },
        {
            a1: '?',
            a2: '条件表达式',
            a3: '如果条件为真 ? 则值为 X : 否则值为 Y ',
        }
    ]}
    bordered
    pagination={false}
/>



## 判断语句
判断结构要求程序员指定一个或多个要评估或测试的条件，以及条件为真时要执行的语句（必需的）和条件为假时要执行的语句（可选的）。C 语言把 <RedSpan>任何非零或非空的值假定为 true，把 零 或 null 假定为 false </RedSpan>

```c title='1. if语句'
if(boolean_expression){
    //如果布尔表达式为真将执行的语句
}
```

```c title='2. if else 语句(扩展 if else if else)'
if(boolean_expression){
    //如果布尔表达式为真将执行的语句
}
```

```c title='3. switch case 语句'
switch(boolean_expression){
    case constant_expression:
        statement(s);
        break;
    case constant_expression:
        statement(s);
        break;
    // 可以有任意数量的 case 语句
    default： // 可选的
        statement(s);
}
```

## 循环语句
循环语句允许我们多次执行一个语句或语句组，C 语言提供了以下几种循环类型：


```c title='1. while 语句'
while(condition){
    statement(s);
}
```

```c title='2. for 语句'
for(init; condition; increment) {
     statement(s);
}
```

```c title='3. do while 语句 (至少保证执行一次循环体)'
do {
    statement(s);
} while (condition);
```

### break 语句

C 语言中的 break 语句有以下两种用法：
1. 当 break 语句出现在 <RedSpan>一个循环内时，循环会立即终止</RedSpan>，且程序流将 <RedSpan>继续执行紧接着循环的下一条语句</RedSpan>

2. 它 <RedSpan>可用于终止 switch 语句中的一个 case </RedSpan>。

如果使用的是嵌套循环（即一个循环内嵌套另一个循环），<RedSpan>break 语句会停止执行最内层的循环</RedSpan>，然后开始执行该块之后的下一行代码。


### continue 语句
C 语言中的 continue 语句会<RedSpan>跳过当前循环中的代码，强迫开始下一次循环</RedSpan>。

<RedSpan>对于 for 循环，continue 语句执行后自增语句仍然会执行</RedSpan>。对于 while 和 do...while 循环，continue 语句重新执行语句判断条件。

### goto 语句

C 语言中的 goto 语句允许<RedSpan>把控制无条件转移到同一函数内的被标记的语句</RedSpan>



## C 作用域
局部变量： 在<RedSpan>某个函数或块内部声明的变量称为局部变量</RedSpan>。它们只能<RedSpan>被函数或该代码块内部的语句使用</RedSpan>。局部变量在函数外部是不可知的。

全局变量是<RedSpan>定义在函数外部，通常是在程序的顶部</RedSpan>。全局变量在<RedSpan>整个程序生命周期内都是有效的</RedSpan>,在任意的函数内部能访问全局变量。全局变量可以被任何函数访问。也就是说，全局变量在声明后整个程序中都是可用的。

在程序中，局部变量和全局变量的名称可以相同，但是 <RedSpan> 在函数内,如果两个名字相同,会使用局部变量 </RedSpan> ，全局变量不会被使用。


## C 内存管理

定义一个指针，必须 <RedSpan>使其指向某个存在的内存空间的地址，才能使用 </RedSpan>，否则使用野指针会造成段错误，内存分配与释放函数如下：

`void free(void *addr);` 该函数释放 addr 所指向的内存块，释放的是动态分配的内存空间。

`void *malloc(int size);`  在堆区分配一块指定大小为 size 的内存空间，用来存放数据，不会被初始化。

## 常用函数
下面是头文件 string.h 中定义函数，掌握以下常用的函数：
* <RedSpan>`void *memcpy(void *dest,const void *src,size_t n)`: 从 src 复制 n 个字符到 dest </RedSpan>。
* <RedSpan>`void *memset(void *str,int c,size_t n)` 复制字符 c （一个无符号字符）到参数 str 所指向的字符串的前 n 个字符 </RedSpan>。
* `char *strcat(char *dest,const char *src)`: 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。
* `char *strncat(char *dest,const char *src,size_t n)`: 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾，直到 n 字符长度为止。
* `char *strchr(const char *str,int c)`:在参数 str 所指向的字符串中搜索第一次出现字符 c （一个无符号字符）的位置。
* <RedSpan>`int strcmp(const char *str1,const char *str2)`: 把 str1 所指向的字符串 和 str2 所指向的字符串进行比较。 </RedSpan>
* <RedSpan>`char strcpy(char *dest,const char *str)`: 把 src 所指向的字符串复制到 dest </RedSpan>
* <RedSpan>`size_t strlen(const char *str)`: 计算字符串 str 的长度，直到空结束字符，但不包括空结束字符 </RedSpan>






